#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <dybuf.h>

DyBuf::DyBuf(int init_size)
    : buf(NULL)
    , buf_left(0), buf_right(0)
    , grow_size(0)
{
    if (init_size < 0)
        init_size = 0;

    buf_size = init_size;

    if (buf_size > 0) {
        buf_size += 16;
        buf = (char *) malloc (buf_size);
    }
}

DyBuf::~DyBuf()
{
    if (buf)
        free(buf);
}

bool DyBuf::ensure_size(int len)
{
    int free_bytes;

    /* (len << 1) < 0.
     * size too large.
     */
    if (len < 0 || (len << 1) < 0) {
        return false;
    }

    free_bytes = buf_size - buf_right;

    /* 额外字节 */
    len += 16;

    if (free_bytes < len) {
        if (grow_size == 0) {
            if (buf_size == 0)
                buf_size = len;
            else {
                while (free_bytes < len) {
                    buf_size <<= 1;

                    free_bytes = buf_size - buf_right;
                }
            }
        }
        else {
            while (free_bytes < len) {
                buf_size += grow_size;

                free_bytes = buf_size - buf_right;
            }
        }

        if (buf)
            buf = (char *) realloc (buf, buf_size);
        else
            buf = (char *) malloc (buf_size);

        if (buf == NULL) {
            buf_left = buf_right = buf_size = 0;
            return false;
        }
    }

    return true;
}

bool DyBuf::append(const void *ptr, int len)
{
    if (len < 0 || ptr == NULL)
        return false;

    if (len == 0)
        return true;

    if (!ensure_size(len))
        return false;

    memcpy(buf + buf_right, ptr, len);

    buf_right += len;

    buf[buf_right] = 0;

    return true;
}

bool DyBuf::assign(const void *ptr, int len)
{
    buf_left = buf_right = 0;

    if (len < 0 || ptr == NULL)
        return false;

    if (len == 0)
        return true;

    if (!ensure_size(len))
        return false;

    memcpy(buf, ptr, len);

    buf_right = len;

    buf[buf_right] = 0;

    return true;
}

void DyBuf::drop_first(int nbytes)
{
    if (nbytes > 0) {
        buf_left += nbytes;
        if (buf_left > buf_right)
            buf_left = buf_right;
    }
}

void DyBuf::drop_last(int nbytes)
{
    if (nbytes > 0) {
        buf_right -= nbytes;
        if (buf_right < buf_left)
            buf_right = buf_left = 0;
    }
}

void DyBuf::move_to_begin()
{
    if (buf_left > 0) {
        memmove(buf, buf + buf_left, buf_right - buf_left);

        buf_right -= buf_left;
        buf_left   = 0;
    }
}

void DyBuf::reset_mem()
{
    if (buf) {
        free(buf);
        buf_left = buf_right = 0;
        buf_size = 0;
        buf = NULL;
    }
}
